'''
Created on Apr 30, 2010

@author: aortlieb
'''
import reportlab
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from cStringIO import StringIO

from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.fonts import addMapping
from reportlab.pdfbase import pdfmetrics

def xClearLeft(leftfield,margin=0):
    if isinstance(leftfield,field):
        def xval(page):
            fontwidth = leftfield.fontSize if leftfield.fontWidth else page.settings.fontWidth
            return margin + (leftfield.x if not isinstance(leftfield.x,type(xClearLeft)) else leftfield.x(page)) + ( (leftfield.maxlen+1) * fontwidth )
        return xval
    else:
        raise TypeError('This method requires a <field> type.')

class field():
    
    def __init__(self,x,y,maxlen=None,fontName=None,fontSize=None,fontWidth=None,map=None,default=None):
        self.x = x
        self.y = y
        self.maxlen = maxlen
        self.fontName = fontName
        self.fontSize = fontSize
        self.fontWidth= fontWidth # FIXME: Determine automatically from the font data somehow.
        self.map = map 
        self.default = default
        self.fieldName = ''
        

class page():
    def __init__(self):
        '''
           Be sure to fill in the settings class...
            
           fontFiles must be an iterable of iterables such that:
           [("VeraMono","VeraMono.ttf"),]
           
           The first fontFile listed will be the default specified for
           any field that does not explicitly specify its own font.
           
           The fontSize will work the same way. 
        '''
                
        self.fields     = []
        self._setupFields()
    
    class settings:
        # This way we can set these attributes declaratively.
        width=0
        height=0
        fontSize=7
        fontWidth=4 # FIXME: Determine automatically from the font data somehow.
        fontFiles=[("VeraMono","VeraMono.ttf"),]
        
    def _setupFields(self):
        self.fields = []
        for f in dir(self):
            if isinstance(getattr(self,f),field):
                fval = getattr(self,f)
                fval.fieldName = f
                self.fields.append(fval)
                setattr(self, f, str())
    
    def _getVal(self,record,field):
        map = str(field.map).strip().split('.')
        data = record
        try:
            for name in map:
                data = getattr(data,name)
        except AttributeError:
            data = field.default
            if not data:
                data = '(?%s)'%field.fieldName
        
        if hasattr(data,'__call__'):
            data = data()
        
        return str(data)
        
    def populate(self,record):
        for field in self.fields:
            setattr(self, field.fieldName, self._getVal(record,field))
    
    def insert(self,c):
            
        c.setPageSize((self.settings.width,self.settings.height))
        
        for f in self.fields:
            c.setFont(  f.fontName if f.fontName else self.settings.fontFiles[0][0]
                        , f.fontSize if f.fontSize else self.settings.fontSize
                      )
            c.drawString(f.x if not isinstance(f.x,type(xClearLeft)) else f.x(self) ,f.y,str(getattr(self,f.fieldName))[0:f.maxlen])
        
        c.showPage()
        
        return c

    def render(self): # FIXME: This should probably never get called.
        for font in self.settings.fontFiles:
            pdfmetrics.registerFont(TTFont(font[0],font[1]))
            addMapping(font[0],0,0,font[0])
            
        buffer = StringIO()
        c      = canvas.Canvas(buffer)
        
        c.setPageSize((self.settings.width,self.settings.height))
        
        for f in self.fields:
            c.setFont(  f.fontName if f.fontName else self.settings.fontFiles[0][0]
                        , f.fontSize if f.fontSize else self.settings.fontSize
                      )
            c.drawString(f.x if not isinstance(f.x,type(xClearLeft)) else f.x(self) ,f.y,str(getattr(self,f.fieldName))[0:f.maxlen])
        c.save()    
        data = buffer.getvalue()
        return data
    
class report(object):
    """
    TODO FIXME Even the design of this class isn't complete.
    Requirements:
        Subclass to define query() which returns a query object.
        Directly map columns of SA object to attributes of a supplied page's fields.
        Use iterations of the query result to render a report in one of two ways:
            report-page-to-physical-page
            report-page-to-physical-page-contents
        Not sure if this can have provisions for sane wrapping of text onto the next page.
    """
    def __init__(self,parameters):
        self.parameters = parameters
        if not getattr(self,'filename',None):
            raise "ReportError: Filename not set"
    
    def registerPageFonts(self):
        fonts = {}
        for pageinfo in self.pages:
            pg = pageinfo[0]
            for font in pg.settings.fontFiles:
                fonts[font[0]]=font[1]
        
        
        for k in fonts.keys():
            pdfmetrics.registerFont(TTFont(k,fonts[k]))
            addMapping(k,0,0,k)
            
    def render(self):
        q = self.query(self.parameters)
        if q.count()>0:
            self.registerPageFonts()
            buffer = StringIO()
            c      = canvas.Canvas(buffer)
            
            for record in q.all():
                for pageinfo in self.pages:
                    pg = pageinfo[0]()
                    pg.populate(record)
                    for cnt in range(0,pageinfo[1]):
                        c = pg.insert(c)
                
            c.save()
            data = buffer.getvalue()
            return data
        else:
            return False
